package com.ict.web.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.ict.common.utils.StringUtils;
import cn.hutool.core.lang.Assert;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ict.web.annotation.ExcelAnnotation;
import com.ict.web.domain.GovernmentDataResources;
import com.ict.web.dto.GovernmentDataResourceAddDto;
import com.ict.web.dto.UploadBatchDto;
import com.ict.web.mapper.GovernmentDataResourcesMapper;
import com.ict.web.service.IGovernmentDataResourcesService;
import com.ict.web.vo.ErrorVo;
import com.ict.web.vo.UploadVo;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;


/**
 * 政务数据资源目录清单--刘杰Service业务层处理
 * 
 * @author ruoyi
 * @date 2022-03-13
 */
@Service
public class GovernmentDataResourcesServiceImpl extends ServiceImpl<GovernmentDataResourcesMapper,GovernmentDataResources> implements IGovernmentDataResourcesService
{
    private static final Logger LOGGER = LoggerFactory.getLogger(GovernmentDataResources.class);
    @Autowired
    private GovernmentDataResourcesMapper governmentDataResourcesMapper;

    /**
     * 查询政务数据资源目录清单--刘杰
     *
     * @param governmentDataId 政务数据资源目录清单--刘杰主键
     * @return 政务数据资源目录清单--刘杰
     */
    @Override
    public GovernmentDataResources selectGovernmentDataResourcesByGovernmentDataId(Long governmentDataId)
    {
        return governmentDataResourcesMapper.selectGovernmentDataResourcesByGovernmentDataId(governmentDataId);
    }

    /**
     * 查询政务数据资源目录清单--刘杰列表
     *
     * @param governmentDataResources 政务数据资源目录清单--刘杰
     * @return 政务数据资源目录清单--刘杰
     */
    @Override
    public List<GovernmentDataResources> selectGovernmentDataResourcesList(GovernmentDataResources governmentDataResources)
    {
        return governmentDataResourcesMapper.selectGovernmentDataResourcesList(governmentDataResources);

    }

    /**
     * 新增政务数据资源目录清单
     * @param programmeId
     * @param dto 政务数据资源目录清单新增对象
     * @return
     */
    @Override
    public int insertGovernmentDataResources(Long programmeId,GovernmentDataResourceAddDto dto) {
        GovernmentDataResources bean = GovernmentDataResourceAddDto.of(dto);
        bean.setProgrammeId(programmeId);
        int result = governmentDataResourcesMapper.insert(bean);
        Assert.isTrue(result>0,"新增政务数据资源目录清单失败！！！");
        return result;
    }

    @Override
    public UploadVo batchInsertFile(MultipartFile file) throws IOException {
        org.springframework.util.Assert.isTrue(file != null, "导入文件不能为空");
        UploadVo uploadVo = new UploadVo();
        //存储错误信息
        List<ErrorVo> errorVoArrayList = new ArrayList<>();
//        //导入目录数量
        dealWithFile(file,uploadVo,errorVoArrayList);
        if(StringUtils.isNotEmpty(uploadVo.getErrorVoList())){
            uploadVo.getErrorVoList().stream().forEach(s->{s.setRowIndex(s.getRowIndex()+1);});
        }
        return uploadVo;
    }
    /**
     * 单次缓存的数据量
     */
    public static final int BATCH_COUNT = 1000;
    /**
     *临时存储UploadBatchDto
     */
    private List<GovernmentDataResources> uploadBatchDtoList = new ArrayList<>();
    /**
     * 处理文件
     */
    public UploadVo dealWithFile(MultipartFile file,UploadVo uploadVo,List<ErrorVo> errorVoArrayList) throws IOException {
        InputStream inputStream = file.getInputStream();
        //读取表头内容
        EasyExcel.read(inputStream, UploadBatchDto.class, new ReadListener<UploadBatchDto>() {
            @Override
            public void invoke(UploadBatchDto data, AnalysisContext context) {
                ErrorVo errorVo = new ErrorVo();
                //读取数据放入临时存储UploadBatchDto
                GovernmentDataResources governmentDataResources = UploadBatchDto.toGovernmentDataResources(data);
                uploadBatchDtoList.add(governmentDataResources);
                //校验
                checkoutCatalog(data, context, errorVoArrayList);
                // 达到BATCH_COUNT了，清空临时存储信息防止数据量过大，容易OOM
                if (uploadBatchDtoList.size() >= BATCH_COUNT){
                    // 存储完成清理临时存储
                    uploadBatchDtoList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                    //返回错误信息
                    errorVo.setColumn("导入失败：");
                    errorVo.setDescription("数据量过大无法导入");
                    errorVoArrayList.add(errorVo);

                }
            }
            //所有数据解析完成了 都会来调用
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                // 确保文件中无报错信息则返回
                if(CollectionUtils.isEmpty(errorVoArrayList)){
                    returnData();
                }
            }
            @Override
            public void onException(Exception exception, AnalysisContext context) {
                LOGGER.error("解析失败，但是继续解析下一行:{}", exception.getMessage());
                // 如果是某一个单元格的转换异常 能获取到具体行号
                ErrorVo errorVo = new ErrorVo();
                // 如果要获取头的信息 配合invokeHeadMap使用
                if (exception instanceof ExcelDataConvertException) {
                    ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
                    LOGGER.error("第{}行，第{}列解析异常", excelDataConvertException.getRowIndex(),
                            excelDataConvertException.getColumnIndex());

                    String cellMsg = "";
                    CellData cellData = excelDataConvertException.getCellData();
                    //这里有一个celldatatype的枚举值,用来判断CellData的数据类型
                    CellDataTypeEnum type = cellData.getType();
                    if (type.equals(CellDataTypeEnum.NUMBER)) {
                        cellMsg = cellData.getNumberValue().toString();
                    } else if (type.equals(CellDataTypeEnum.STRING)) {
                        cellMsg = cellData.getStringValue();
                    } else if (type.equals(CellDataTypeEnum.BOOLEAN)) {
                        cellMsg = cellData.getBooleanValue().toString();
                    }
                    String errorMsg = String.format("第%s行,第%s列,数据值为:%s,该数据值不符合要求,请检验后重新导入!", excelDataConvertException.getRowIndex() + 1, excelDataConvertException.getColumnIndex(), cellMsg);
                    errorVo.setRowIndex(excelDataConvertException.getRowIndex());
                    errorVo.setColumn(String.valueOf(excelDataConvertException.getColumnIndex()));
                    errorVo.setDescription(errorMsg);
                    errorVoArrayList.add(errorVo);
                }

            }
            /**
             * 返回导入数据
             */
            private void returnData() {
                if(CollectionUtils.isNotEmpty(uploadBatchDtoList)){
                    uploadVo.setGovernmentDataResourcesList(uploadBatchDtoList);
                    // 存储完成清理临时存储
                    uploadBatchDtoList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
                LOGGER.info("返回导入数据成功！");
            }
        }).headRowNumber(3).sheet().doRead();
        return uploadVo;
    }
    /**
     * 校验字段是否为空
     */
    public void checkoutCatalog(UploadBatchDto dto, AnalysisContext context, List<ErrorVo> errorVoArrayList) {
        ErrorVo errorVo = new ErrorVo();
        //当前行数
        Integer rowIndex = context.readRowHolder().getRowIndex();
        //校验非空字段
        Field[] fields = dto.getClass().getDeclaredFields();
        List<String> list = Stream.of("platformDirectoryId","platformDirectoryId","other").collect(Collectors.toList());
        //需要校验非空的字段
        List<Field> checkoutColumnList = Arrays.stream(fields).filter(item -> !list.contains(item.getName())).collect(Collectors.toList());
        Object fieldValue = null;
        for (Field field : checkoutColumnList) {
            try {
                //成员变量为private
                field.setAccessible(true);
                //得到属性值
                fieldValue = field.get(dto);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            //只要有一个属性值为null 就返回false 表示对象属性有为null的
            if (fieldValue == null) {
                //返回错误信息
                errorVo.setRowIndex(rowIndex);
                errorVo.setColumn(field.getAnnotation(ExcelAnnotation.class).message());
                errorVo.setDescription(field.getAnnotation(ExcelAnnotation.class).message()+"为空");
                errorVoArrayList.add(errorVo);
            }
        }

    }
}
